/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.manager.actions;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;

import org.igfay.jfig.JFig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.webflow.execution.RequestContext;

import uk.ljmu.qudexapps.manager.domain.FilesModel;
import uk.ljmu.qudexapps.manager.exceptions.ExcelManagementException;
import uk.ljmu.qudexapps.manager.services.SpreadsheetService;
import uk.ljmu.qudexapps.manager.utilities.ExcelManagement;
import uk.ljmu.qudexapps.manager.utilities.GeneralUtilities;
import uk.ljmu.qudexapps.xmllib.exceptions.FoxmlException;

public class FileActions {

    private String fileSeparator = System.getProperty("file.separator");
    @Autowired
    private SpreadsheetService spService;

    /**
     * @param spService
     *            the spService to set
     */
    public void setSpService(SpreadsheetService spService) {
	this.spService = spService;
    }

    /**
     * @return the spService
     */
    public SpreadsheetService getSpService() {
	return spService;
    }

    public String processSpreadsheet(RequestContext context) throws Exception {
	String resultCode;

	FilesModel files = (FilesModel) context.getFlowScope()
		.get("filesModel");

	if (files.getZipFiles() != null && files.getZipFiles().size() > 0) {
	    resultCode = processFileSpreadsheetWithContents(context);
	} else {
	    resultCode = processFileSpreadsheet(context);
	}

	return resultCode;
    }

    /**
     * POJO which processes a collection-level spreadsheet and creates the
     * collection(s) in the Fedora repository
     * 
     * @param context
     *            flowContextRequest object
     * @return "success" or "error" strings
     * 
     * @throws Exception
     *             WebFlow exceptions not captured
     */
    public String processFileSpreadsheet(RequestContext context)
	    throws Exception {

	FilesModel files = (FilesModel) context.getFlowScope()
		.get("filesModel");
	// Holds Error messages in a clean way so actions themselves will handle
	// business logic exceptions and then
	// the action will redirect with proper error codes + messages -->
	// error() or success()
	MessageContext messages = context.getMessageContext();
	this.spService.setFileType(files.getSpreadsheet().getContentType());
	this.spService.getFiles().setCollectionsIds(files.getCollectionsIds());
	this.spService.getFiles().setSpreadsheet(files.getSpreadsheet());
	this.spService.setQudexCollections(false);
	try {
	    String fullPath = (files.getTempDirPath()
		    .endsWith(this.fileSeparator)) ? files.getTempDirPath()
		    + files.getSpreadsheet().getOriginalFilename() : files
		    .getTempDirPath()
		    + this.fileSeparator
		    + files.getSpreadsheet().getOriginalFilename();
	    File file = new File(fullPath);
	    this.writeToFile(files.getSpreadsheet().getInputStream(), file);
	    String completeUrl = JFig.getInstance().getValue("fedoraServer",
		    "fileServletUrl");
	    fullPath = completeUrl + "?fileName="
		    + files.getSpreadsheet().getOriginalFilename() + "&type=";
	    this.spService.setOriginalSpreadsheetPath(fullPath);
	    spService.ingestFilesSpreadsheet();
	    // After ingest clean
	    spService.setOriginalSpreadsheetPath("");
	    if (!(file.delete())) {
		 messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.collection.IO").build());
		 return "error";
	    }
	    spService.getFiles().deleteTemporaryFiles();
	    spService.getFiles().setCollectionsIds("");
	    context.getFlowScope().put("results", this.spService.getResults());
	    return "success";
	} catch (FoxmlException ex) {
	    // Manage service logic exceptions
	    messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.files.General").build());
	    return "error";
	} catch (IOException e) {
	    messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.files.IO").build());
	    return "error";
	} catch (Exception e) {
	    messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.files.Other").build());
	    return "error";
	}
    }

    /**
     * POJO which processes a collection-level spreadsheet and creates the
     * documents in the Fedora repository. A ZIP file with document copies is
     * provided
     * 
     * @param context
     *            flowContextRequest object
     * @return "success" or "error" strings
     * 
     * @throws Exception
     *             WebFlow exceptions not captured
     */
    public String processFileSpreadsheetWithContents(RequestContext context)
	    throws Exception {

	FilesModel files = (FilesModel) context.getFlowScope()
		.get("filesModel");
	File sessionTempDir;
	HttpServletRequest request = (HttpServletRequest) context
		.getExternalContext().getNativeRequest();
	// Holds Error messages in a clean way so actions themselves will handle
	// business logic exceptions and then
	// the action will redirect with proper error codes + messages -->
	// error() or success()
	MessageContext messages = context.getMessageContext();
	this.spService.setFileType(files.getSpreadsheet().getContentType());
	this.spService.getFiles().setCollectionsIds(files.getCollectionsIds());
	this.spService.getFiles().setSpreadsheet(files.getSpreadsheet());
	this.spService.getFiles().setZipFiles(files.getZipFiles());

	ExcelManagement spreadsheetManager = new ExcelManagement();

	try {
	    sessionTempDir = (File) request.getSession().getServletContext()
		    .getAttribute("javax.servlet.context.tempdir");
	    if (sessionTempDir == null) {
		throw new ExcelManagementException(
			"Temporary directory for uploads not found...");
	    }

	    String modifiedSpreadsheetPath = "";
	    // Decompress
	    GeneralUtilities.decompressZipFile(this.spService.getFiles()
		    .getTempDirPath(), this.spService.getFiles().getZipFiles());
	    modifiedSpreadsheetPath = spreadsheetManager.fillExcelFile(
		    this.spService.getFiles().getSpreadsheet(), sessionTempDir
			    .getAbsolutePath(), this.spService.getFiles()
			    .getTempDirName(), this.spService.getFiles()
			    .getTempDirPath());
	    this.spService.getFiles().setModifiedSpreadsheet(
		    new FileInputStream(new File(modifiedSpreadsheetPath)));
	    String fullPath = (files.getTempDirPath()
		    .endsWith(this.fileSeparator)) ? files.getTempDirPath()
		    + files.getSpreadsheet().getOriginalFilename() + "COPY" : files
		    .getTempDirPath()
		    + this.fileSeparator
		    + files.getSpreadsheet().getOriginalFilename() + "COPY";
	    File file = new File(fullPath);
	    this.writeToFile(files.getSpreadsheet().getInputStream(), file);
	    String completeUrl = JFig.getInstance().getValue("fedoraServer",
		    "fileServletUrl");
	    fullPath = completeUrl + "?fileName="
		    + files.getSpreadsheet().getOriginalFilename() + "&type=";
	    this.spService.setOriginalSpreadsheetPath(fullPath);
	    spService.ingestFilesSpreadsheet();
	    // After ingest clean
	    spService.setOriginalSpreadsheetPath("");
	    if (!(file.delete())) {
		 messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.collection.IO").build());
		 return "error";
	    }
	    spService.getFiles().setCollectionsIds("");	    
	    context.getFlowScope().put("results", this.spService.getResults());
	    return "success";
	} catch (IOException e1) {
	    messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.files.IO").build());
	    return "error";
	} catch (ExcelManagementException e1) {
	    // messages.addMessage(new
	    // MessageBuilder().error().code("foxmlError.files.General").build());
	    messages.addMessage(new MessageBuilder().error().defaultText(
		    e1.getMessage()).build());
	    return "error";
	} catch (Exception e) {
	    messages.addMessage(new MessageBuilder().error().code(
		    "foxmlError.files.Other").build());
	    return "error";
	} finally {
		if (files.getModifiedSpreadsheet() != null) {
			files.getModifiedSpreadsheet().close();
			files.setModifiedSpreadsheet(null);
		}
	    if (files.getZipFiles().size() > 0)
		files.cleanZipFiles();
	    spService.getFiles().deleteTemporaryFiles();
	}
    }

    private void writeToFile(InputStream is, File file) {
	try {
	    DataOutputStream out = new DataOutputStream(
		    new BufferedOutputStream(new FileOutputStream(file)));
	    int c;
	    while ((c = is.read()) != -1) {
		out.writeByte(c);
	    }
	    is.close();
	    out.close();
	} catch (IOException e) {
	    System.err.println("Error Writing/Reading Streams.");
	}
    }
}
